#ifndef MYMUDUO_BASE_DATE_H
#define MYMUDUO_BASE_DATE_H

#include "mymuduo/base/copyable.h"
#include "mymuduo/base/Types.h"

struct tm;

namespace mymuduo
{

    ///
    /// Date in Gregorian calendar.
    ///
    /// This class is immutable.
    /// It's recommended to pass it by value, since it's passed in register on x64.
    /// 用于Julian Day和普通年月日的转换
    ///
    class Date : public mymuduo::copyable
    // public boost::less_than_comparable<Date>,
    // public boost::equality_comparable<Date>
    {
    public:
        struct YearMonthDay
        {
            int year;  // [1900..2500]
            int month; // [1..12]
            int day;   // [1..31]
        };

        static const int kDaysPerWeek = 7;
        static const int kJulianDayOf1970_01_01;

        ///
        /// Constucts an invalid Date.
        ///
        Date() : julianDayNumber_(0) {}

        ///
        /// Constucts a yyyy-mm-dd Date.
        ///
        /// 1 <= month <= 12
        Date(int year, int month, int day);

        ///
        /// Constucts a Date from Julian Day Number.
        ///
        explicit Date(int julianDayNum) : julianDayNumber_(julianDayNum) {}

        ///
        /// Constucts a Date from struct tm
        ///
        explicit Date(const struct tm &);

        // default copy/assignment/dtor are Okay

        void swap(Date &that) { std::swap(julianDayNumber_, that.julianDayNumber_); }

        bool valid() const { return julianDayNumber_ > 0; }

        ///
        /// Converts to yyyy-mm-dd format.
        ///
        string toIsoString() const;

        struct YearMonthDay yearMonthDay() const;

        int year() const { return yearMonthDay().year; }

        int month() const { return yearMonthDay().month; }

        int day() const { return yearMonthDay().day; }

        // [0, 1, ..., 6] => [Sunday, Monday, ..., Saturday ]
        int weekDay() const { return (julianDayNumber_ + 1) % kDaysPerWeek; }

        int julianDayNumber() const { return julianDayNumber_; }

    private:
        int julianDayNumber_;
    };

    inline bool operator<(Date x, Date y) { return x.julianDayNumber() < y.julianDayNumber(); }

    inline bool operator==(Date x, Date y) { return x.julianDayNumber() == y.julianDayNumber(); }
}

#endif
